﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EngineAdmin
{
    /// <summary>
    /// Internal engine facade
    /// </summary>
    internal class InternalEngineFacade : EngineFacadeBase,  IEngineProgress
    {
        private Engine _engine;
        private GameStateConfig _config;

        public InternalEngineFacade(Engine engine)
        {
            _engine = engine;
            
            // set events
            _engine.InternalEngine.BackgroundProcessInit += InternalEngine_BackgroundProcessInit;
            _engine.InternalEngine.ProgressReport += InternalEngine_ProgressReport;
            _engine.InternalEngine.BackgroundProcessFinished += InternalEngine_BackgroundProcessFinished;
            _engine.InternalEngine.MoveComputed += InternalEngine_MoveComputed;

        }

        /// <summary>
        /// Starts the move computing with the Daphne internal engine
        /// </summary>
        public override void ComputeMove(GameStateConfig gameState)
        {
            _config = gameState;

            // convert models
            var checkersBoard = EngineMoveHelper.ConvertBoard(gameState);
            var gameHistory = EngineMoveHelper.ConvertMoveHistory(gameState);

            // call the method for computing the move by the Daphne internal engine
            _engine.InternalEngine.ComputeMove(gameState.CheckersType, checkersBoard, gameState.WhiteToMove,
                            _engine.EngineConfiguration.InternalEngineSettings, gameHistory);
        }

        #region Callback API

        public event EventHandler StartEvent;

        public event EventHandler<EngineProgressEventArgs> ProgressEvent;

        public event EventHandler<EngineFinishedEventArgs> FinishedEvent;

        #endregion


        #region Added Wrapper handlers

        void InternalEngine_MoveComputed(object sender, ComputerEngine.MoveComputedEventArgs e)
        {
            if (FinishedEvent != null)
            {
                // convert move and pass on event
                EngineFinishedEventArgs args = new EngineFinishedEventArgs();
                args.Move = ConvertMove(e.OutputBitBoard);
                
                // convert engine output
                args.Output = new EngineMoveOutput();
                args.Output.CacheHit = e.OutputBitBoard.CacheHit;
                args.Output.MaxDepth = e.OutputBitBoard.Depth;
                args.Output.MaxCalmPositionDepth = e.OutputBitBoard.CalmPosDepth;
                args.Output.NumberOfNodes = e.OutputBitBoard.NumberOfNodes;
                args.Output.NodesPerSecond = e.OutputBitBoard.NodesPerSecond;

                FinishedEvent(this, args);
            }
        }

        void InternalEngine_BackgroundProcessFinished(object sender, EventArgs e)
        {
            // TO-DO debug - set completed event
        }

        void InternalEngine_ProgressReport(object sender, ComputerEngine.ProgressReportEventArgs e)
        {
            // pass on percents progress
            if (ProgressEvent != null)
            {
                EngineProgressEventArgs args = new EngineProgressEventArgs();
                args.Percents = e.ProgressPercent;
                ProgressEvent(this, args);
            }
            
        }

        void InternalEngine_BackgroundProcessInit(object sender, ComputerEngine.BackgroundProcessEventArgs e)
        {
            // TO-DO: complete loading endgame database
            if (e.BackgroundProcessType == ComputerEngine.eBackgroundProcess.Thinking)
            {
                // pass on event
                if (StartEvent != null)
                {
                    StartEvent(this, EventArgs.Empty);
                }
            }
        }

        #endregion

        #region Helper Methods

        /// <summary>
        /// Move conversion routine
        /// </summary>
        private BoardPos[] ConvertMove(ComputerEngine.OutputBitBoard engineMove)
        {
            // convert the move from engine
            List<Position> move = EngineMoveHelper.Convert(engineMove, _config);

            // do model conversion
            return move.Select(p => new BoardPos(p.Row, p.Col)).ToArray();
        }

        #endregion


        public override int GetOpeningMoveCount()
        {
            return _engine.InternalEngine.GetOpeningCount();
        }

        public override List<BoardPos[]> GetOpeningByIndex(int index, GameStateConfig config)
        {
            _config = config;

            // get opening by index
            var moves = _engine.InternalEngine.GetOpeningByIndex(index);

            // convert model
            List<BoardPos[]> moveList = new List<BoardPos[]>();
            foreach (var move in moves)
            {
                var posList = ConvertMove(move);
                moveList.Add(posList);
                
                // DEBUG - lousy do move - taking into account only non-jumps
                config.WhiteToMove = !config.WhiteToMove;
                config.CheckersBoard[posList[1].Row, posList[1].Col]
                    = config.CheckersBoard[posList[0].Row, posList[0].Col];
                config.CheckersBoard[posList[0].Row, posList[0].Col] = eFigure.None;              
            }

            return moveList;
        }
    }
}
